package consul

import (
	"encoding/json"
	"fmt"
	"net/http"
	"shyxy-net/common/schema"
	"time"
)

type Server struct {
	name      string
	discovery *ServiceDiscovery
	client    *http.Client
}

func NewDiscovery(consulAddr, serviceName string, policy *PolicyConfig) *Server {
	discovery := NewServiceDiscovery(consulAddr, serviceName, policy)
	return &Server{
		name:      serviceName,
		discovery: discovery,
		client:    http.DefaultClient,
	}
}

func (s *Server) GetServiceInstance() (map[string]interface{}, error) {
	timeout := time.After(5 * time.Second)
	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()
	for {
		select {
		case <-timeout:
			return nil, fmt.Errorf("Server.GetServiceInstance:timeout")
		case <-ticker.C:
			instance, err := s.discovery.GetOneInstance()
			if err == nil {
				return instance, nil
			}
		}
	}
}

func (s *Server) Call(method, path string, result interface{}) error {
	instance, err := s.GetServiceInstance()
	if err != nil {
		return err
	}
	service, ok := instance["Service"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("Server.Call:invalid service data structure")
	}
	addressInterface, ok := service["Address"]
	if !ok {
		return fmt.Errorf("Server.Call:service address not found")
	}
	address, ok := addressInterface.(string)
	if !ok {
		return fmt.Errorf("Server.Call:service address is not a string")
	}

	portInterface, ok := service["Port"]
	if !ok {
		return fmt.Errorf("Server.Call:service port not found")
	}
	port, err := convertInterfaceToInt(portInterface)
	if err != nil {
		return fmt.Errorf("Server.Call:%w", err)
	}
	url := schema.URL(schema.HTTP, address, port, path)
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return err
	}
	resp, err := s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Server.Call:consul call failed with status: %s", resp.Status)
	}
	return json.NewDecoder(resp.Body).Decode(result)
}

func (s *Server) Get(path string, result interface{}) error {
	return s.Call("GET", path, result)
}

func (s *Server) Post(path string, result interface{}) error {
	return s.Call("POST", path, result)
}

func convertInterfaceToInt(i interface{}) (int, error) {
	var result int
	var err error
	switch v := i.(type) {
	case float64:
		result = int(v)
	case int:
		result = v
	case int64:
		result = int(v)
	case float32:
		result = int(v)
	case uint:
		result = int(v)
	case uint64:
		result = int(v)
	default:
		err = fmt.Errorf("unexpected type: %T", v)
		return 0, err
	}
	return result, nil
}
